package utils;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Scanner;

public class CategoriesInfo implements Serializable
{
    private HashMap<String, HashSet<String>> categoriesMap;

    /**
     * load a CategoriesInfo instance from the specified file
     * 
     * @param f
     * @return
     * @throws FileNotFoundException
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static CategoriesInfo load(File f) throws FileNotFoundException,
            IOException, ClassNotFoundException
    {
        CategoriesInfo result;
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
        result = (CategoriesInfo) ois.readObject();
        ois.close();
        return result;
    }

    /**
     * return a CategoriesInfo using the specified hashMap
     * 
     * @param categoriesMap
     * @return
     */
    public static CategoriesInfo createCategoriesInfo(
            HashMap<String, HashSet<String>> categoriesMap)
    {
        CategoriesInfo ci = new CategoriesInfo();
        ci.setCategoriesMap(categoriesMap);
        return ci;
    }

    private CategoriesInfo()
    {
    }

    public CategoriesInfo(File file, String splitter)
            throws FileNotFoundException
    {
        categoriesMap = new HashMap<String, HashSet<String>>();
        Scanner scan = new Scanner(file);
        scan.useDelimiter(splitter);
        while (scan.hasNextInt())
        {
            String id = "" + scan.nextInt();
            String categories = "" + scan.nextInt();
            if (!categoriesMap.containsKey(categories))
                categoriesMap.put(categories, new HashSet<String>());
            categoriesMap.get(categories).add(id);
        }
    }

    public CategoriesInfo(File direction)
    {
        categoriesMap = new HashMap<String, HashSet<String>>();
        File[] dirs = direction.listFiles(new FileFilter()
        {
            @Override
            public boolean accept(File pathname)
            {
                return pathname.isDirectory();
            }
        });
        for (File dir : dirs)
        {
            HashSet<String> set = new HashSet<String>();
            File[] files = dir.listFiles();
            for (File file : files)
            {
                String name = file.getName();
                if (name.toLowerCase().endsWith(".xml"))
                {
                    name = name.replaceAll(".xml", "");
                    set.add(name);
                }
            }
            categoriesMap.put(dir.getName(), set);
        }
    }

    /**
     * evaluate this categories information with the standard categories.
     * 
     * @param actualInfo
     * @return
     */
    public double evaluateWith(CategoriesInfo actualInfo)
    {
        double result = 0;
        Collection<HashSet<String>> c1 = this.getCategoriesCollection();
        Collection<HashSet<String>> c2 = actualInfo.getCategoriesCollection();
        int totalCount = 0;
        for (HashSet<String> set1 : c1)
        {
            totalCount += set1.size();
            double max = -1;
            for (HashSet<String> set2 : c2)
            {
                int sameElements = 0;
                for (String str : set2)
                {
                    if (set1.contains(str))
                        sameElements++;
                }
                if (max < sameElements)
                    max = sameElements;
            }
            result += max;
        }
        result /= totalCount;
        return result;
    }

    /**
     * save the current instance as a file
     * 
     * @param f
     * @throws FileNotFoundException
     * @throws IOException
     */
    public void save(File f) throws FileNotFoundException, IOException
    {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
        oos.writeObject(this);
        oos.flush();
        oos.close();
    }

    /**
     * @return the categoriesMap
     */
    public HashMap<String, HashSet<String>> getCategoriesMap()
    {
        return categoriesMap;
    }

    public Collection<HashSet<String>> getCategoriesCollection()
    {
        return categoriesMap.values();
    }

    /**
     * @param categoriesMap
     *            the categoriesMap to set
     */
    public void setCategoriesMap(HashMap<String, HashSet<String>> categoriesMap)
    {
        this.categoriesMap = categoriesMap;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((categoriesMap == null) ? 0 : categoriesMap.hashCode());
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj)
    {
        if (this == obj) { return true; }
        if (obj == null) { return false; }
        if (!(obj instanceof CategoriesInfo)) { return false; }
        CategoriesInfo other = (CategoriesInfo) obj;
        if (categoriesMap == null)
        {
            if (other.categoriesMap != null) { return false; }
        }
        else if (!categoriesMap.equals(other.categoriesMap)) { return false; }
        return true;
    }

    public static void main(String[] args) throws IOException,
            ClassNotFoundException
    {
        CategoriesInfo c1 = new CategoriesInfo(new File(
                "D:/INEX Dataset/train_categories.csv"), "[\t\r\n]+");
        System.out.println(c1.getCategoriesMap().size());
        c1.save(new File("train_categories.info"));
        CategoriesInfo c2 = new CategoriesInfo(new File(
                "D:/INEX Dataset/test set"));
        // c2.categoriesMap.remove("1474166");
        // System.out.println(c1.evaluateWith(c2));
    }
}
